<template>
    <div
        class="a-input-slider"
        :class="[
            `color-${myColor}`,
            `size-${mySize}`,
            { sharp: mySharp },
            { disabled: myDisabled },
        ]"
        :tabindex="myDisabled ? 'unset' : '0'"
    >
        <div class="slider-container" ref="sliderContainer">
            <a-tooltip
                :tips="`${value}`"
                :popShown="popShown"
                :size="mySize"
                :sharp="mySharp"
            >
                <div
                    class="slider"
                    ref="slider"
                    :style="{ left: `${sliderLeft}` }"
                >
                    <div class="slider-bg"></div>
                </div>
            </a-tooltip>
            <div class="min-line line"></div>
            <div class="max-line line"></div>
            <span class="min-text" :class="[`c-font-size-${mySize}`]">{{
                minValue
            }}</span>
            <span class="max-text" :class="[`c-font-size-${mySize}`]">{{
                maxValue
            }}</span>
        </div>
    </div>
</template>

<style lang="scss" scoped>
@import "../scss/vars";

.a-input-slider {
    width: 200px;
    box-sizing: border-box;
    overflow: hidden;
    outline: none;

    &.disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    @each $colorPackageName, $colorPackageValue in $colorPackages {
        $frontColor: map-get($colorPackageValue, "frontColor");
        $bgColor: map-get($colorPackageValue, "bgColor");

        &.color-#{$colorPackageName} {
            color: $frontColor;
            background-color: $bgColor;

            .slider-bg,
            .line {
                background-color: $frontColor;
            }
        }
    }

    @each $sizePackageName, $sizePackageValue in $sizePackages {
        &.size-#{$sizePackageName} {
            padding-left: map-get($sizePackageValue, "space");
            padding-right: map-get($sizePackageValue, "space");
            height: map-get($sizePackageValue, "size");
            &:not(.sharp) {
                border-radius: map-get($sizePackageValue, "borderRadius");
            }
        }
    }

    .slider-container {
        position: relative;
        width: 100%;
        height: 100%;
        .slider {
            position: absolute;
            top: 0;
            left: 0;
            width: 1px;
            height: 100%;
            .slider-bg {
                width: 5px;
                height: 100%;
                margin-left: -2px;
                box-shadow: 5px 0 10px #333;
            }
        }
        .min-text {
            position: absolute;
            left: 0;
            bottom: 0;
            pointer-events: none;
        }
        .max-text {
            position: absolute;
            right: 0;
            bottom: 0;
            pointer-events: none;
        }
        .line {
            position: absolute;
            width: 1px;
            height: 50%;
            top: 0;
        }
        .min-line {
            left: 0;
        }
        .max-line {
            left: 100%;
        }
    }
}
</style>

<script>
import APopup from './APopup.vue'
import utils from "../common/utils"
import inputMixin from "../mixins/inputMixin"
import { Decimal } from 'decimal.js'

export default {
    name: 'AInputSlider',
    components: { APopup },
    mixins: [inputMixin],
    props: {
        value: {
            type: Number,
            default: null
        },
        minValue: {
            type: Number,
            default: 0,
        },
        maxValue: {
            type: Number,
            default: 100,
        },
        step: {
            type: Number,
            default: 1,
        },
    },
    data() {
        return {
            mouseDown: false,
            containerLeft: 0,
            containerRight: 0,
            focused: false,
            mouseIn: false,
            resizeObserver: new ResizeObserver(this.checkAndSetContainerPositionAndSize),
        }
    },
    computed: {
        sliderLeft() {
            let rate = (this.value - this.minValue) / (this.maxValue - this.minValue)
            return rate * (this.maxClientX - this.minClientX) + 2 + 'px'
        },
        decimalCount() {
            return Math.max(utils.getDecimalCount(this.minValue), utils.getDecimalCount(this.maxValue), utils.getDecimalCount(this.step))
        },
        popShown() {
            if (this.focused) {
                return true
            }
            if (this.mouseIn) {
                return true
            }
            return null
        },
        minClientX() {
            return this.containerLeft + 2
        },
        maxClientX() {
            return this.containerRight - 2
        }
    },
    mounted() {
        this.resizeObserver.observe(this.$refs.sliderContainer)
        this.addEventListener()
    },
    unmounted() {
        this.resizeObserver.disconnect()
        this.removeEventListener()
    },
    methods: {
        input(val) {
            this.$emit('update:value', val)
        },
        addEventListener() {
            this.$el.addEventListener('mousedown', this.onRootMousedown)
            this.$el.addEventListener('focus', this.onRootFocus)
            this.$el.addEventListener('blur', this.onRootBlur)
            this.$el.addEventListener('mouseover', this.onRootMouseover)
            this.$el.addEventListener('mouseleave', this.onRootMouseleave)
            window.document.addEventListener('mousemove', this.onDocumentMousemove)
            window.document.addEventListener('mouseup', this.onDocumentMouseup)
            window.document.addEventListener('keydown', this.onDocumentKeydown)
        },
        removeEventListener() {
            window.document.removeEventListener('mousemove', this.onDocumentMousemove)
            window.document.removeEventListener('mouseup', this.onDocumentMouseup)
            window.document.removeEventListener('keydown', this.onDocumentKeydown)
        },
        onRootMousedown(e) {
            if (this.myDisabled) {
                return
            }
            if (e.button != 0) {
                return
            }
            this.mouseDown = true

            this.calcValue(e.clientX)
        },
        onDocumentMousemove(e) {
            if (!this.mouseDown) {
                return
            }
            e.preventDefault()
            this.calcValue(e.clientX)
        },
        onDocumentMouseup(e) {
            this.mouseDown = false
        },
        onRootFocus() {
            this.focused = true
        },
        onRootBlur() {
            this.focused = false
        },
        onRootMouseover() {
            this.mouseIn = true
        },
        onRootMouseleave() {
            this.mouseIn = false
        },
        onDocumentKeydown(e) {
            if (!this.focused) {
                return
            }
            if (e.keyCode == 37) {
                e.preventDefault()
                let value = new Decimal(this.value ?? 0).minus(new Decimal(this.step)).toNumber()
                this.input(Math.max(value, this.minValue))
            } else if (e.keyCode == 39) {
                e.preventDefault()
                let value = new Decimal(this.value ?? 0).plus(new Decimal(this.step)).toNumber()
                this.input(Math.min(value, this.maxValue))
            }
        },
        checkAndSetContainerPositionAndSize() {
            let containerRect = this.$refs.sliderContainer.getBoundingClientRect()
            this.containerLeft = containerRect.left
            this.containerRight = containerRect.right
        },
        calcValue(mouseClientX) {
            let realValue = (mouseClientX - this.minClientX) * (this.maxValue - this.minValue) / (this.maxClientX - this.minClientX) + this.minValue
            if (realValue < this.minValue) {
                this.input(this.minValue)
            } else if (realValue > this.maxValue) {
                this.input(this.maxValue)
            } else {
                let outputValue = Math.round(realValue / this.step) * this.step
                let fixedOutputValue = outputValue.toFixed(this.decimalCount) * 1
                this.input(fixedOutputValue)
            }
        },
    }
}
</script>